package top.fullj.remoting.provider;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.fullj.remoting.message.InvokeRequest;
import top.fullj.remoting.message.InvokeResponse;
import top.fullj.remoting.message.Message;

/**
 * @author bruce.wu
 * @since 2022/2/14 14:39
 */
@ChannelHandler.Sharable
public class ServiceHandler extends SimpleChannelInboundHandler<Message> {

    private static final Logger LOG = LoggerFactory.getLogger(ServiceHandler.class);

    private final ServiceRegistry serviceRegistry;

    public ServiceHandler() {
        serviceRegistry = ServiceRegistry.getInstance();
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Message msg) throws Exception {
        //TODO verify message
        //TODO customized message interceptor chain after receive
        LOG.trace("MSG: " + msg);
        Object payload = msg.getPayload();
        Object response = null;
        if (payload instanceof InvokeRequest) {
            response = onInvokeRequest((InvokeRequest) payload);
        }
        Message reply = new Message(response);
        reply.setId(msg.getId());
        ctx.writeAndFlush(reply);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        LOG.info("connection open: local={}, remote={}",
                ctx.channel().localAddress(), ctx.channel().remoteAddress());
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        LOG.info("connection lost: local={}, remote={}",
                ctx.channel().localAddress(), ctx.channel().remoteAddress());
    }

    private InvokeResponse onInvokeRequest(InvokeRequest request) {
        ServiceDelegate delegate = serviceRegistry.lookup(request.getService());
        if (delegate == null) {
            return new InvokeResponse("Service not found: " + request.getService());
        }
        try {
            Object result = delegate.invoke(request.getMethod(),
                    request.getArgs(), request.getTypes());
            return new InvokeResponse(result);
        } catch (Throwable tr) {
            LOG.error("invoke {}.{} error!", request.getService(), request.getMethod(), tr);
            return new InvokeResponse(tr);
        }
    }

}
